﻿using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using MongoDB.Bson.Serialization;

namespace anydata.Entitys
{
    public class EntityBase
    {
        public EntityBase()
        {
            status = 1;
            version = 1;
            isDeleted = false;
            createTime = DateTime.Now;
            updateTime = DateTime.Now;
            labels = new List<string>();
            extensions = new Dictionary<string, object>();
        }
        /// <summary>
        /// 雪花ID
        /// </summary>
        [BsonId]
        public string id { get; set; }
        /// <summary>
        ///  名称
        /// </summary>
        public string name { get; set; }
        /// <summary>
        ///  状态
        /// </summary>
        public int status { get; set; }
        /// <summary>
        ///  创建人员ID
        /// </summary>
        public string createUser { get; set; }
        /// <summary>
        ///  更新人员ID
        /// </summary>
        public string updateUser { get; set; }
        /// <summary>
        /// 修改次数
        /// </summary>
        public int version { get; set; }
        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime createTime { get; set; }
        /// <summary>
        /// 更新时间
        /// </summary>
        public DateTime updateTime { get; set; }
        /// <summary>
        /// 软删除标记
        /// </summary>
        public bool isDeleted { get; set; }
        /// <summary>
        /// 标签集
        /// </summary>
        public List<string> labels { get; set; }
        /// <summary>
        /// 拓展数据
        /// </summary>
        [BsonExtraElements]
        [JsonExtensionData]
        public IDictionary<string, object> extensions { get; set; }
        /// <summary>
        /// 转成实体对象
        /// </summary>
        /// <param name="data">BsonDocument 数据</param>
        /// <param name="userId">当前操作用户</param>
        /// <returns></returns>
        public static List<EntityBase> FromJToken(JToken data, string userId, out bool isArray)
        {
            var list = new List<EntityBase>();
            isArray = false;
            switch (data.Type)
            {
                case JTokenType.Object:
                    list.Add(FromJObject<EntityBase>(data, userId));
                    break;
                case JTokenType.Array:
                    foreach (var item in data)
                    {
                        if (item.Type == JTokenType.Object)
                        {
                            list.Add(FromJObject<EntityBase>(item, userId));
                        }
                    }
                    isArray = true;
                    break;
            }
            return list;
        }
        public static T FromJObject<T>(JToken data, string userId) where T : EntityBase
        {
            var entity = BsonSerializer.Deserialize<T>(data.ToBDocument());
            if (entity.extensions.ContainsKey("id"))
            {
                entity.id = entity.extensions["id"].ToString();
                entity.extensions.Remove("id");
            }
            if (string.IsNullOrEmpty(entity.createUser))
            {
                entity.createUser = userId;
            }
            entity.updateUser = userId;
            entity.updateTime = DateTime.Now;
            return entity;
        }

        protected static readonly string[] selfProps = new[]
        {
            nameof(id),
            nameof(name),
            nameof(status),
            nameof(createUser),
            nameof(updateUser),
            nameof(createTime),
            nameof(updateTime),
            nameof(version),
            nameof(isDeleted),
            nameof(labels),
        };

        public string[] GetProperties()
        {
            return selfProps
                .Concat(extensions.Keys)
                .ToArray();
        }

        /// <summary>
        /// 通过索引器读写任意属性（包括内置的和拓展的）
        /// </summary>
        /// <param name="property">属性名，可以不存在</param>
        /// <returns>属性值</returns>
        public object this[string property]
        {
            get
            {
                if (selfProps.Contains(property))
                {
                    //
                    // 尽量不使用反射，会降低性能
                    return property switch
                    {
                        nameof(id) => id,
                        nameof(name) => name,
                        nameof(status) => status,
                        nameof(createUser) => createUser,
                        nameof(updateUser) => updateUser,
                        nameof(createTime) => createTime,
                        nameof(updateTime) => updateTime,
                        nameof(version) => version,
                        nameof(isDeleted) => isDeleted,
                        nameof(labels) => labels,
                        _ => null,
                    };
                }
                return extensions.ContainsKey(property) ? extensions[property] : null;
            }

            set
            {
                if (selfProps.Contains(property))
                {
                    object? ret = (property, value) switch
                    {
                        (nameof(id), string v) => id = v,
                        (nameof(name), string v) => name = v,
                        (nameof(status), int v) => status = v,
                        (nameof(createUser), string v) => createUser = v,
                        (nameof(updateUser), string v) => updateUser = v,
                        (nameof(createTime), DateTime v) => createTime = v,
                        (nameof(updateTime), DateTime v) => updateTime = v,
                        (nameof(version), int v) => version = v,
                        (nameof(isDeleted), bool v) => isDeleted = v,
                        (nameof(labels), List<string> v) => labels = v,
                        _ => null,
                    };
                    if (ret is null)
                    {
                        var type = GetType().GetProperty(property)!;
                        if (type.PropertyType.IsClass)
                        {
                            type.SetValue(this, null);
                            return;
                        }
                        throw new ArgumentException($"属性 {property} 赋值类型错误");
                    }
                }
                else
                {
                    extensions[property] = value;
                }

            }
        }

        /// <summary>
        /// 浅克隆对象
        /// </summary>
        /// <returns>克隆的对象</returns>
        public EntityBase Clone()
        {
            var clone = new EntityBase();
            foreach (var prop in GetProperties())
            {
                clone[prop] = this[prop];
            }
            return clone;
        }

    }
}
